from random import *
import numpy as np
from copy import deepcopy

class Board:
  def __init__(self, nrows, ncols, rule=(2,3,3), init_alive=0.5):
    self.nrows = nrows
    self.ncols = ncols
    self.state_ = np.empty( (nrows, ncols), dtype=np.int8 )
    self.new_state_ = np.empty( (nrows, ncols), dtype=np.int8 )
    self.updated = [ ]
    (self.lonely, self.reproduce, self.overcrowded) = rule
    self.init_alive = init_alive

  def _set_state(self, i, j, x):
    self.state_[i][j] = x

  def _get_state(self, i, j):
    return self.state_[i][j] 

  def initialize(self):
    for i in range(0,self.nrows):
      for j in range(0,self.ncols):
        self._set_state(i,j, 1 if random() > self.init_alive else 0)



  def check_stasis(self, renderer,screen, board, update,steps=10, render_off=True):
    def step_and_render():
      board.step()
      renderer.render( screen, board )
      update()
    def copy_step_and_render():
      s = deepcopy( self.state_ )
      if not render_off:
        step_and_render()
      else:
        board.step()
      return s
    s1 = copy_step_and_render() 
    s2 = copy_step_and_render() 
    s3 = copy_step_and_render() 
    return (s1  == s2).all() or (s1 == s3).all()

  def step(self):

    ir1 = np.arange(0, self.nrows)[:, None]
    ir0 = np.r_[ir1[-1:], ir1[:-1]]
    ir2 = np.r_[ir1[1:], ir1[:1]]

    ic1 = np.arange(0, self.ncols)
    ic0 = np.r_[ic1[-1:], ic1[:-1]]
    ic2 = np.r_[ic1[1:], ic1[:1]]

    # Count neighbours.
    nn =self.state_[ir0, ic0] +self.state_[ir0, ic1] +self.state_[ir0, ic2] + \
        self.state_[ir1, ic0] +               0 +self.state_[ir1, ic2] + \
        self.state_[ir2, ic0] +self.state_[ir2, ic1] +self.state_[ir2, ic2]

    self.new_state_ =self.state_.copy()

    i_dead = np.where(((nn < self.lonely) | (nn > self.overcrowded)))
    i_live = np.where((nn == self.reproduce))

    i_static = np.where(((nn >= self.lonely) & (nn <= self.overcrowded) & (nn != self.reproduce)))

    self.new_state_[i_dead] = 0
    self.new_state_[i_live] = 1
    #i_alive = np.where( ((self.state_ == 1) & (self.new_state_ == 1)))
    #self.new_state_[ i_alive ] = 


    self.updated =  zip(*np.where( (self.new_state_ != self.state_) ))
    temp = self.state_
    self.state_ = self.new_state_
    self.new_state_ = temp
